package com.atlin.util;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.Security;
import java.util.Base64;

/**
 * 数据加解密/sm4
 */
public class SM4Encryptor {

    private static final String ALGORITHM_NAME = "SM4";
    private static final String TRANSFORMATION = "SM4/CBC/PKCS5Padding";
    private static final String PROVIDER = "BC";
    private static final String CHARSET = "UTF-8";
    private static final String SECRET_KEY = "dsa-fwf232@wwe1!";
    private static final String IV = "df009&*232@wwe1!";

    static {
        // 注册国密算法提供者
        if (Security.getProvider(PROVIDER) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    public static void main(String[] args) {
        String message = "$88\n"
                + "t11RhFUoILP9vyYtvej3yS8TEL5KfljJD1sysFfKZjQZl/qfclgzMNw4MO57JQzu61bd71LDdJLJIGTAUwYlQA==\n"
                + " ack\n";
        String secretKey = "HZZXZ@0123456789";
        String iv = "HZZXZ@0123456789";

        // 清理字符串格式
        message = message.replace("\nack", " ack")
                .replace("\n$", " $")
                .replace("\n", " ")
                .replace("  ", " ")
                .trim();

        // 第二个字段是加密串
        String encryptedMsg = message.split(" ")[1];
        String decryptMsg = decryptCBC(encryptedMsg, secretKey, iv);

        System.out.println("📦 原始加密串: " + encryptedMsg);
        System.out.println("🔓 解密结果: " + decryptMsg);

        // 测试加密
        String testPlain = "国密SM4测试123!";
        String cipherText = encryptCBC(testPlain, secretKey, iv);
        System.out.println("🧩 加密结果: " + cipherText);
        System.out.println("✅ 解密验证: " + decryptCBC(cipherText, secretKey, iv));
    }


    public static String decryptCBC(String encryptedMsg) {
        return decryptCBC(encryptedMsg, SECRET_KEY, IV);
    }
    /**
     * SM4 CBC 模式解密（Base64输入）
     */
    public static String decryptCBC(String encryptedMsg, String secretKey, String iv) {
        try {
            validateKey(secretKey);
            validateIV(iv);

            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(Charset.forName(CHARSET)), ALGORITHM_NAME);
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(Charset.forName(CHARSET)));
            Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedMsg));
            return new String(decrypted, Charset.forName(CHARSET));
        } catch (Exception e) {
            throw new RuntimeException("❌ SM4 CBC解密失败", e);
        }
    }


    public static String encryptCBC(String plainText) {
        return encryptCBC(plainText, SECRET_KEY, IV);
    }
    
    /**
     * SM4 CBC 模式加密（返回Base64）
     */
    public static String encryptCBC(String plainText, String secretKey, String iv) {
        try {
            validateKey(secretKey);
            validateIV(iv);

            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(Charset.forName(CHARSET)), ALGORITHM_NAME);
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(Charset.forName(CHARSET)));
            Cipher cipher = Cipher.getInstance(TRANSFORMATION, PROVIDER);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

            byte[] encrypted = cipher.doFinal(plainText.getBytes(Charset.forName(CHARSET)));
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new RuntimeException("❌ SM4 CBC加密失败", e);
        }
    }

    /**
     * 校验密钥长度（16字节 = 128位）
     */
    private static void validateKey(String key) {
        if (key == null || key.getBytes(Charset.forName(CHARSET)).length != 16) {
            throw new IllegalArgumentException("SM4密钥长度必须为16字节");
        }
    }

    /**
     * 校验IV长度（16字节）
     */
    private static void validateIV(String iv) {
        if (iv == null || iv.getBytes(Charset.forName(CHARSET)).length != 16) {
            throw new IllegalArgumentException("SM4 CBC模式的IV长度必须为16字节");
        }
    }
}
